/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.reflection.ExceptionUtil;

/**
 * @author Clinton Begin
 */
/**
 * 插件,用的代理模式
 *
 */
public class Plugin implements InvocationHandler {
	/**
	 * 需要被代理的实例
	 */
	private Object target;
	/**
	 * 拦截器实例
	 */
	private Interceptor interceptor;
	/**
	 * 拦截器需要拦截的方法摘要，这里Class键为Executor等上述的四个值为需要被拦截的方法
	 */
	private Map<Class<?>, Set<Method>> signatureMap;

	// 此类不能直接创建，需要通过静态方法wrap来创建代理类
	private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
		this.target = target;
		this.interceptor = interceptor;
		this.signatureMap = signatureMap;
	}
	/**
	 * 代理
	 * @param target 需要被代理的实例
	 * @param interceptor 拦截器实例
	 * @return
	 */
	public static Object wrap(Object target, Interceptor interceptor) {
		// 获取需要被代理类的所有待拦截的接口
		Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
		// 取得要改变行为的类(ParameterHandler|ResultSetHandler|StatementHandler|Executor)
		Class<?> type = target.getClass();
		// 取得接口
		Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
		// 产生代理
		if (interfaces.length > 0) {
			 // 创建代理类
			 //对type的代理 
			return Proxy.newProxyInstance(type.getClassLoader(), interfaces,
					new Plugin(target, interceptor, signatureMap));
		}
		// 没有需要拦截的方法，直接返回原实例
		return target;
	}
	// 在代理类中调用
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		try {
			// 看看如何拦截
			Set<Method> methods = signatureMap.get(method.getDeclaringClass());
			  // 判断是否为待拦截方法，这里为动态判断，所有在拦截器多的时候，会影响性能
			if (methods != null && methods.contains(method)) {
				// 调用Interceptor.intercept，也即插入了我们自己的逻辑
				//拦截，----插入自己的逻辑
				return interceptor.intercept(new Invocation(target, method, args));
			}
			// 最后还是执行原来逻辑
			return method.invoke(target, args);
		} catch (Exception e) {
			throw ExceptionUtil.unwrapThrowable(e);
		}
	}

	/**
	 *  取得签名Map ----获取需要被拦截的方法摘要
	 * @param interceptor 拦截器实例
	 * @return
	 */
	private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
		// 取Intercepts注解，例子可参见ExamplePlugin.java
		 // 先获取拦截器实现类上的注解，提取需要被拦截的方法
		 /* 注解示例：@Intercepts(value={@Signature(args={Void.class},method="query",type=Void.class)})*/
		Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
		// issue #251
		// 必须得有Intercepts注解，没有报错
		if (interceptsAnnotation == null) {
			throw new PluginException(
					"No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
		}
		// value是数组型，Signature的数组
		Signature[] sigs = interceptsAnnotation.value();
		// 每个class里有多个Method需要被拦截,所以这么定义
		Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
		for (Signature sig : sigs) {
			Set<Method> methods = signatureMap.get(sig.type());
			if (methods == null) {
				methods = new HashSet<Method>();
				signatureMap.put(sig.type(), methods);
			}
			try {
				// 根据方法名以及参数获取待拦截方法
				Method method = sig.type().getMethod(sig.method(), sig.args());
				methods.add(method);
			} catch (NoSuchMethodException e) {
				throw new PluginException(
						"Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
			}
		}
		return signatureMap;
	}

	/**
	 *  取得接口
	 * @param type  类型	
	 * @param signatureMap 签名Map
	 * @return
	 */
	private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
		Set<Class<?>> interfaces = new HashSet<Class<?>>();
		while (type != null) {
			for (Class<?> c : type.getInterfaces()) {
				// 貌似只能拦截ParameterHandler|ResultSetHandler|StatementHandler|Executor
				// 拦截其他的无效
				// 当然我们可以覆盖Plugin.wrap方法，达到拦截其他类的功能
				if (signatureMap.containsKey(c)) {
					interfaces.add(c);
				}
			}
			type = type.getSuperclass();
		}
		return interfaces.toArray(new Class<?>[interfaces.size()]);
	}

}
